﻿namespace embPDFUtils
{
    using System.Text.RegularExpressions;



    using iTextSharp.text;
    using iTextSharp.text.pdf;
    using iTextSharp.text.pdf.codec;

    using SPDCMScanning.FileSystem.Helpers;

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Diagnostics.Contracts;

    using Rectangle = iTextSharp.text.Rectangle;

    /// <summary>
    /// Utility Class to append,replace,and merge contents,
    /// other documents into new or existing pdf files.
    /// </summary>
    public class PdfDocumentToolsItextSharp : IPdfDocumentTools
    {
        #region Private Members

        /// <summary>
        /// Holds the FileHelper-Helper
        /// </summary>
        private FileHelper _helper = new FileHelper();

        #endregion

        #region Public Members

        /// <summary>
        /// Appends the single image to existing PDF.
        /// </summary>
        /// <param name="sourceFileDirectory">The source file directory.</param>
        /// <param name="sourceFileName">Name of the source file.</param>
        /// <param name="destinationFileDirectory">The destination file directory.</param>
        /// <param name="destinationFileName">Name of the destination file.</param>
        /// <param name="imageFileName">Name of the image file.</param>
        public void AppendSingleImageToExistingPdf(
            string sourceFileDirectory,
            string sourceFileName,
            string destinationFileDirectory,
            string destinationFileName,
            string imageFileName)
        {
            if (string.IsNullOrEmpty(sourceFileDirectory))
            {
                throw new ArgumentException("Parameter must not be null or empty.", sourceFileDirectory);
            }
            if (string.IsNullOrEmpty(sourceFileName))
            {
                throw new ArgumentException("Parameter must not be null or empty.", sourceFileName);
            }
            if (string.IsNullOrEmpty(destinationFileDirectory))
            {
                throw new ArgumentException("Parameter must not be null or empty.", destinationFileDirectory);
            }
            if (string.IsNullOrEmpty(destinationFileName))
            {
                throw new ArgumentException("Parameter must not be null or empty.", destinationFileName);
            }
            if (string.IsNullOrEmpty(imageFileName))
            {
                throw new ArgumentException("Parameter must not be null or empty.", imageFileName);
            }

            string oldFile = String.Empty;
            string newFile = String.Empty;

            if (!File.Exists(Path.Combine(sourceFileDirectory, sourceFileName)))
            {
                throw new FileNotFoundException(
                    "File does not exist.", Path.Combine(sourceFileDirectory, sourceFileName));
            }
            if (!File.Exists(imageFileName))
            {
                throw new FileNotFoundException("File does not exist.", imageFileName);
            }

            //Check for a argument exception (invalid path characters)
            try
            {
                oldFile = Path.Combine(sourceFileDirectory, sourceFileName);
                newFile = Path.Combine(destinationFileDirectory, destinationFileName);
            }
            catch (ArgumentException)
            {
                throw;
            }

            PdfReader reader;
            //Check for bad suprises like invalid PDF-Files,corrupted, files to process with password, etc.
            try
            {
                reader = new PdfReader(new RandomAccessFileOrArray(oldFile), null);
            }
            catch (iTextSharp.text.exceptions.InvalidPdfException)
            {
                throw;
            }
            catch (iTextSharp.text.exceptions.BadPasswordException)
            {
                throw;
            }
            catch (iTextSharp.text.exceptions.IllegalPdfSyntaxException)
            {
                throw;
            }

            iTextSharp.text.Rectangle size = reader.GetPageSizeWithRotation(1);
            Document document = new Document(size);
            FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
            PdfWriter writer = PdfWriter.GetInstance(document, fs);
            document.Open();
            PdfContentByte cb = writer.DirectContent;
            for (int pageNumber = 1; pageNumber < (reader.NumberOfPages + 1); pageNumber++)
            {
                document.NewPage();
                if (pageNumber == 1)
                {
                    Chunk fileRef = new Chunk(" ");
                    fileRef.SetLocalDestination(oldFile);
                    document.Add(fileRef);
                }
                PdfImportedPage page = writer.GetImportedPage(reader, pageNumber);
                int rotation = reader.GetPageRotation(pageNumber);
                if ((rotation == 90) || (rotation == 270))
                {
                    cb.AddTemplate(page, 0f, -1f, 1f, 0f, 0f, reader.GetPageSizeWithRotation(pageNumber).Height);
                }
                else
                {
                    page.Width = document.PageSize.Width;
                    page.Height = document.PageSize.Height;
                    cb.AddTemplate(page, 0.99f, 0f, 0f, 1f, 0f, 0f);
                }
            }
            iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(imageFileName);
            img.ScaleAbsolute(size.Width, size.Height);
            document.NewPage();
            cb.AddImage(img, size.Width, 0f, 0f, size.Height, 0f, 0f);
            document.Close();
            fs.Close();
            writer.Close();
            reader.Close();
        }

        /// <summary>
        /// Appends the single image to existing PDF copy version.
        /// </summary>
        /// <param name="sourceFileDirectory">The source file directory.</param>
        /// <param name="sourceFileName">Name of the source file.</param>
        /// <param name="destinationFileDirectory">The destination file directory.</param>
        /// <param name="destinationFileName">Name of the destination file.</param>
        /// <param name="endProduct">The end product.</param>
        /// <param name="imageFileName">Name of the image file.</param>
        public void AppendSingleImageToExistingPdfCopyVersion(
            string sourceFileDirectory,
            string sourceFileName,
            string destinationFileDirectory,
            string destinationFileName,
            string endProduct,
            string imageFileName)
        {
            if (string.IsNullOrEmpty(sourceFileDirectory))
            {
                throw new ArgumentException("Parameter must not be null or empty.", sourceFileDirectory);
            }
            if (string.IsNullOrEmpty(sourceFileName))
            {
                throw new ArgumentException("Parameter must not be null or empty.", sourceFileName);
            }
            if (string.IsNullOrEmpty(destinationFileDirectory))
            {
                throw new ArgumentException("Parameter must not be null or empty.", destinationFileDirectory);
            }
            if (string.IsNullOrEmpty(destinationFileName))
            {
                throw new ArgumentException("Parameter must not be null or empty.", destinationFileName);
            }
            if (string.IsNullOrEmpty(imageFileName))
            {
                throw new ArgumentException("Parameter must not be null or empty.", imageFileName);
            }

            string sourceFile = string.Empty;
            string destinationFile = string.Empty;

            try
            {
                sourceFile = Path.Combine(sourceFileDirectory, sourceFileName);
                destinationFile = Path.Combine(destinationFileDirectory, destinationFileName);
            }
            catch (ArgumentException)
            {
                throw;
            }

            if (!File.Exists(sourceFile))
            {
                throw new FileNotFoundException("File does not exist.", sourceFile);
            }
            if (!File.Exists(imageFileName))
            {
                throw new FileNotFoundException("File does not exist.", imageFileName);
            }

            string oldFile = string.Empty;
            string newFile = string.Empty;

            oldFile = sourceFile;
            newFile = destinationFile;

            PdfReader reader;
            try
            {
                reader = new PdfReader(new RandomAccessFileOrArray(oldFile), null);
            }
            catch (iTextSharp.text.exceptions.InvalidPdfException)
            {
                throw;
            }
            catch (iTextSharp.text.exceptions.BadPasswordException)
            {
                throw;
            }
            catch (iTextSharp.text.exceptions.IllegalPdfSyntaxException)
            {
                throw;
            }

            iTextSharp.text.Rectangle size = reader.GetPageSizeWithRotation(1);
            Document document = new Document(size);
            PdfCopy copy = new PdfCopy(document, new FileStream(newFile, FileMode.Create));
            document.Open();
            iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(imageFileName);
            img.ScaleAbsolute(size.Width, size.Height);
            int pageNumber = 0;
            for (pageNumber = 1; pageNumber < (reader.NumberOfPages + 1); pageNumber++)
            {
                PdfImportedPage p = copy.GetImportedPage(reader, pageNumber);
                copy.AddPage(p);
            }
            copy.AddPage(size, 0);
            document.Close();
            reader.Close();
            copy.Close();
            Document doc = new Document(size);
            FileStream fs = new FileStream(
                Path.Combine(destinationFileDirectory, endProduct), FileMode.Create, FileAccess.Write);
            PdfReader rd = new PdfReader(newFile);
            PdfStamper writer = new PdfStamper(rd, fs);
            writer.GetOverContent(rd.NumberOfPages).AddImage(img, size.Width, 0f, 0f, size.Height, 0f, 0f);
            doc.Close();
            writer.Close();
            rd.Close();
        }

        /// <summary>
        /// Byteses to bitmap.
        /// </summary>
        /// <param name="byteArray">The byte array.</param>
        /// <returns></returns>
        public static Bitmap BytesToBitmap(byte[] byteArray)
        {
            using (MemoryStream ms = new MemoryStream(byteArray))
            {
                return (Bitmap)System.Drawing.Image.FromStream(ms);
            }
        }

        /// <summary>
        /// Creates the PDF.
        /// </summary>
        /// <param name="imageData">The image data.</param>
        /// <param name="pageSize">Size of the page.</param>
        /// <returns></returns>
        public byte[] CreatePdf(byte[] imageData, iTextSharp.text.Rectangle pageSize)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                Document pDoc = new Document(pageSize, 0f, 0f, 0f, 0f);
                PdfWriter writer = PdfWriter.GetInstance(pDoc, ms);
                pDoc.Open();
                PdfContentByte cb = writer.DirectContent;
                RandomAccessFileOrArray rfa = new RandomAccessFileOrArray(imageData);
                iTextSharp.text.Image img = TiffImage.GetTiffImage(rfa, 1);
                img.SetAbsolutePosition(0f, 0f);
                img.ScaleAbsolute(pageSize.Width, pageSize.Height);
                cb.AddImage(img);
                pDoc.Close();
                return ms.GetBuffer();
            }
        }

        /// <summary>
        /// Creates the PDF from multiple files.
        /// </summary>
        /// <param name="size">The size.</param>
        /// <param name="directory">The directory.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="imagePath">The image path.</param>
        /// <param name="filter">The filter.</param>
        public void CreatePdfFromMultipleImageFiles(
            iTextSharp.text.Rectangle size, string directory, string fileName, string imagePath, string filter = "*.*")
        {
            if (size == null)
            {
                throw new ArgumentNullException("Parameter must not be null", "size");
            }
            if (string.IsNullOrEmpty(directory))
            {
                throw new ArgumentException("Parameter must not be null or empty", directory);
            }
            if (!Directory.Exists(directory))
            {
                throw new DirectoryNotFoundException(string.Format("Direcory {0} could not be found.", directory));
            }
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException("Parameter must not be null", "filename");
            }
            if (string.IsNullOrEmpty(imagePath))
            {
                throw new ArgumentException("Parameter must not be null.", "imagePath");
            }
            if (!Directory.Exists(imagePath))
            {
                throw new DirectoryNotFoundException(string.Format("Image {0} could not be found.", imagePath));
            }
            Document doc = new Document(size, 0f, 0f, 0f, 0f);

            string fullPath;

            try
            {
                fullPath = Path.Combine(directory, fileName);
            }
            catch (ArgumentException)
            {
                throw;
            }

            PdfWriter.GetInstance(doc, new FileStream(fullPath, FileMode.Create));
            doc.Open();
            doc.NewPage();
            List<string> fileNames = this._helper.GetFullPathOfAllFilesInDirectory(imagePath, filter);
            foreach (string currentFile in fileNames)
            {
                iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(currentFile);
                image.ScaleToFit(doc.PageSize.Width, doc.PageSize.Height);
                doc.Add(image);
                doc.NewPage();
            }
            doc.Close();
        }

        /// <summary>
        /// Creates the PDF from single file.
        /// </summary>
        /// <param name="size">The size.</param>
        /// <param name="directory">The directory.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="imagePath">The image path.</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
        public void CreatePdfFromSingleImageFile(
            iTextSharp.text.Rectangle size, string directory, string fileName, string imagePath, bool overwrite)
        {
            iTextSharp.text.Image image;
            if (size == null)
            {
                throw new ArgumentNullException("Parameter must not be null", "size");
            }
            if (string.IsNullOrEmpty(directory))
            {
                throw new ArgumentException("Parameter must not be null or empty", directory);
            }
            if (!Directory.Exists(directory))
            {
                throw new DirectoryNotFoundException(string.Format("Direcory {0} could not be found.", directory));
            }
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException("Parameter must not be null", "filename");
            }
            if (string.IsNullOrEmpty(imagePath))
            {
                throw new ArgumentException("Parameter must not be null.", "imagePath");
            }
            if (!File.Exists(imagePath))
            {
                throw new FileNotFoundException(string.Format("Image {0} could not be found.", imagePath));
            }
            Document doc = new Document(size, 0f, 0f, 0f, 0f);
            string fullPath = Path.Combine(directory, fileName);
            if (overwrite)
            {
                PdfWriter.GetInstance(doc, new FileStream(fullPath, FileMode.Create));
                image = iTextSharp.text.Image.GetInstance(imagePath);
            }
            else
            {
                PdfWriter.GetInstance(doc, new FileStream(fullPath, FileMode.Open));
                doc.NewPage();
                image = iTextSharp.text.Image.GetInstance(imagePath);
            }
            doc.Open();
            doc.Add(image);
            doc.Close();
        }

        /// <summary>
        /// Images to byte array.
        /// </summary>
        /// <param name="imageIn">The image in.</param>
        /// <returns></returns>
        public byte[] ImageToByteArray(System.Drawing.Image imageIn)
        {
            MemoryStream ms = new MemoryStream();
            imageIn.Save(ms, ImageFormat.Tiff);
            return ms.ToArray();
        }

        /// <summary>
        /// Extracts the PDF page range.
        /// </summary>
        /// <param name="pageRanges">The page ranges.</param>
        /// <param name="sourceFileDirectory">The source file directory.</param>
        /// <param name="sourceFilename">The source filename.</param>
        /// <param name="destinationFileDirectoy">The destination file directoy.</param>
        /// <param name="destinationFileName">Name of the destination file.</param>
        /// <returns>The full path of the destination file.</returns>
        public string ExtractPdfPageRange(string pageRanges, string sourceFileDirectory, string sourceFilename, string destinationFileDirectoy, string destinationFileName)
        {
            #region Parameter Checks
            if (String.IsNullOrEmpty(pageRanges))
            {
                throw new ArgumentException("Argument must not be null or empty.", "pageRanges");
            }
            if (String.IsNullOrEmpty(pageRanges))
            {
                throw new ArgumentException("Argument must not be null or empty.", "pageRanges");
            }
            
            if (String.IsNullOrEmpty(sourceFileDirectory))
            {
                throw new ArgumentException("Argument must not be null or empty.", "sourceFileDirectory");
            }
            
            if (String.IsNullOrEmpty(sourceFilename))
            {
                throw new ArgumentException("Argument must not be null or empty.", "sourceFilename");
            }
            
            if (String.IsNullOrEmpty(destinationFileName))
            {
                throw new ArgumentException("Argument must not be null or empty.", "destinationFileName");
            }

            
            if (String.IsNullOrEmpty(destinationFileName))
            {
                throw new ArgumentException("Argument must not be null or empty.", "destinationFileName");
            }

            if (!Directory.Exists(sourceFileDirectory))
            {
                throw new DirectoryNotFoundException(
                    String.Format("Directory {0} could not be found.", sourceFileDirectory));
            }

            try
            {
                if (!File.Exists(Path.Combine(sourceFileDirectory, sourceFilename)))
                {
                    throw new FileNotFoundException("File does not exist.", sourceFilename);
                }
            }
            catch (ArgumentException)
            {
                
                throw;
            }
            
            Regex checkPageListings = new Regex("[,0-9-]");
            
            if (!checkPageListings.IsMatch(pageRanges))
            {
                throw new ArgumentException("Argument must only contain the characters ',','-' and '0-9'", pageRanges);
            }

            #endregion

            #region Implementation
            string sourceFile = string.Empty;
            string destinationFile = string.Empty;

            //Prepare the files
            sourceFile = Path.Combine(sourceFileDirectory, sourceFilename);
            destinationFile = Path.Combine(destinationFileDirectoy, destinationFileName);
            PdfReader reader;
            try
            {
                reader = new PdfReader(new RandomAccessFileOrArray(sourceFile), null);
            }
            catch (Exception)
            {

                throw;
            }

            PdfCopy copy;

            //Read only the page range given
            reader.SelectPages(pageRanges);
            iTextSharp.text.Rectangle size = reader.GetPageSizeWithRotation(1);
            Document document = new Document(size);
            //Get the page count
            int numberOfPages = reader.NumberOfPages;


            copy = new PdfCopy(document, new FileStream(destinationFile, FileMode.Create));

            document.Open();

            int totalPageCount = numberOfPages + 1;

            for (int i = 1; i < totalPageCount; i++)
            {
                copy.AddPage(copy.GetImportedPage(reader, i));
            } 
            #endregion


            #region cleanup
            document.Close();
            reader.Close();
            copy.Close(); 
            #endregion

            return destinationFile;
        }

        /// <summary>
        /// Concatenates the multiple PDF files.
        /// </summary>
        /// <param name="sourceFiles">The source files.</param>
        /// <param name="destinationFilePath">The destination file path.</param>
        /// <param name="destinationFileName">Name of the destination file.</param>
        /// <returns></returns>
        public string ConcatenateMultiplePdfFiles(List<string> sourceFiles, string destinationFilePath, string destinationFileName)
        {

            #region Parameter Checks
            if (String.IsNullOrEmpty(destinationFilePath))
            {
                throw new ArgumentException("Argument must not be null or empty.", "destinationFilePath");
            }


            if (String.IsNullOrEmpty(destinationFileName))
            {
                throw new ArgumentException("Argument must not be null or empty.", "destinationFileName");
            }

            if (sourceFiles == null)
            {
                throw new ArgumentException("Argument must not be null or empty", "sourceFiles");
            }

            if (sourceFiles.Count <= 0)
            {
                throw new ArgumentException("Argument must contain entries.", "sourceFiles");
            }

            if (!Directory.Exists(destinationFilePath))
            {
                throw new DirectoryNotFoundException(
                    String.Format("Directory {0} could not be found.", destinationFilePath));
            } 
            #endregion

            string destination = Path.Combine(destinationFilePath, destinationFileName);

             Document document = new Document();
             FileStream output = new FileStream(destination,FileMode.Create);

            try
            {
                // Initialize pdf writer
                PdfWriter writer = PdfWriter.GetInstance(document, output);
                //writer.PageEvent = new PdfPageEvents();
                
                // Open document to write
                document.Open();
                PdfContentByte content = writer.DirectContent;

                // Iterate through all pdf documents
                foreach (var file in sourceFiles)
                {
                    // Create pdf reader
                    PdfReader reader = new PdfReader(file);
                    int numberOfPages = reader.NumberOfPages;

                    // Iterate through all pages
                    for (int currentPageIndex = 1; currentPageIndex <= 
                                       numberOfPages; currentPageIndex++)
                    {
                        // Determine page size for the current page
                        document.SetPageSize(
                           reader.GetPageSizeWithRotation(currentPageIndex));
                        
                        // Create page
                        document.NewPage();

                        
                        PdfImportedPage importedPage = 
                          writer.GetImportedPage(reader, currentPageIndex);

                        
                        // Determine page orientation
                        int pageOrientation = reader.GetPageRotation(currentPageIndex);
                        if ((pageOrientation == 90) || (pageOrientation == 270))
                        {
                            content.AddTemplate(importedPage, 0, -1f, 1f, 0, 0,
                               reader.GetPageSizeWithRotation(currentPageIndex).Height);
                        }
                        else
                        {
                            content.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                throw new Exception("There has an unexpected exception" + 
                      " occured during the pdf merging process.", exception);
            }
            {
                document.Close();
            }
              return destination;
        }

        /// <summary>
        /// Gets the size of the document page.
        /// </summary>
        /// <param name="fullPathToPdf">The full path to PDF.</param>
        /// <returns></returns>
        public Rectangle GetDocumentPageSize(string fullPathToPdf)
        {
            if (String.IsNullOrEmpty(fullPathToPdf))
            {
                throw new ArgumentException("Parameter must not be null or empty", "fullPathToPdf");
            }
            if (!File.Exists(fullPathToPdf))
            {
                throw new FileNotFoundException("File does not exist.", "fullPathToPdf");
            }

            //Open the document and get the page size from the first page
            PdfReader reader = new PdfReader(fullPathToPdf);
            Rectangle currentDocSize = reader.GetPageSize(1);
            //Free resources
            reader.Close();

            return currentDocSize;

        }


        /// <summary>
        /// Gets the PDF page count.
        /// </summary>
        /// <param name="filepath">The filepath.</param>
        /// <returns></returns>
        public int GetPdfPageCount(string filepath)
        {
            int page_count = 0;

            try
            {
                //check for the extension                 
                string extension = Path.GetExtension(filepath);
                if (extension != null && extension.ToUpper().Equals(".PDF"))
                {
                    //Create instance for the PDF reader 
                    var pdfFile = new PdfReader(filepath);
                    //read it's pagecount 
                    page_count = pdfFile.NumberOfPages;
                    //close the file 
                    pdfFile.Close();
                }
            }
            catch (PdfException ex)
            {
                throw ex;
            }
            return page_count;
        }

        #endregion


        
    }
}